//
// Game Animation.
// Copyright (C) 2007 Department of Computer Science, University of Copenhagen
//
#include <animation/configuration.h>

#define DEFINE_GLUT_MAIN
#include <OpenTissue/utility/glut/glut_perspective_view_application.h>
#undef DEFINE_GLUT_MAIN


#include <OpenTissue/utility/gl/gl_util.h>
#include <OpenTissue/gpu/cg/cg_util.h>
#include <OpenTissue/gpu/cg/cg_program.h>

#include <OpenTissue/core/containers/mesh/trimesh/trimesh.h>
#include <OpenTissue/core/containers/mesh/common/io/mesh_obj_read.h>
#include <OpenTissue/core/containers/mesh/common/util/mesh_compute_angle_weighted_vertex_normals.h>

#include <animation/geometry/mesh2geometry.h>
#include <animation/game_animation_types.h>


class Application 
  : public OpenTissue::glut::BasePerspectiveViewApplication< OpenTissue::math::BasicMathTypes<float,size_t> >
{
protected:

  typedef OpenTissue::glut::BasePerspectiveViewApplication< OpenTissue::math::BasicMathTypes<float,size_t> > base_class;

  typedef animation::GameAnimationTypes< math_types >   types;

  typedef types::real_type              real_type;
  typedef types::vector3_type           vector3_type;
  typedef types::quaternion_type        quaternion_type;
  typedef types::matrix3x3_type         matrix3x3_type;

  OpenTissue::cg::Program       m_vertex_program;
  OpenTissue::cg::Program       m_fragment_program;
  types::geometry_type        m_boid_geometry;

  bool m_action[256];          ///< Boolean array used to keep track of the user actions/selections.
  bool                        m_perpixel_lighting; ///< Flag used to toggle per-vertex and per-pixel lighting.

public:

  Application()
  {
    //--- This is how one could change the default size of the main window
    this->width() = 512;
    this->height() = 512;
  }

public:

  char const * do_get_title() const { return "Game Animation Lighting Demo Application"; }

  void do_display()
  {
    //--- pre-render, setup!
    m_vertex_program.enable();
    m_vertex_program.set_modelview_projection_matrix();
    m_vertex_program.set_modelview_inverse_transpose();
    if(!m_perpixel_lighting)
      m_vertex_program.set_float_param("light_vector",1.0,1.0,1.0,0.0);

    m_fragment_program.enable();
    if(m_perpixel_lighting)
      m_fragment_program.set_float_param("light_vector",1.0,1.0,1.0,0.0);

    //--- rendering
    m_boid_geometry.vertex_object_render();

    //--- post-rendering, cleanup!
    m_fragment_program.disable();
    m_vertex_program.disable();
  }

  void do_action(unsigned char choice)
  {
    // Toggle state
    m_action[choice] = ! m_action[choice];
    switch ( choice )
    {
    case 'f':
      {
        m_perpixel_lighting =! m_perpixel_lighting;
        if(m_perpixel_lighting)
        {
          m_vertex_program.load_from_file(
            OpenTissue::cg::Program::vertex_program,demo_path + "lighting/data/vp_fragment_lighting.cg"
            );
          m_fragment_program.load_from_file(
            OpenTissue::cg::Program::fragment_program,demo_path +"lighting/data/fp_fragment_lighting.cg"
            );
        }
        else
        {
          m_vertex_program.load_from_file(
            OpenTissue::cg::Program::vertex_program,demo_path +"lighting/data/vp_vertex_lighting.cg"
            );
          m_fragment_program.load_from_file(
            OpenTissue::cg::Program::fragment_program,demo_path +"lighting/data/fp_vertex_lighting.cg"
            );
        }
      }
      break;
    };
  }

  void do_init_right_click_menu(int main_menu, void menu(int entry))
  {
    int controls = glutCreateMenu( menu );
    glutAddMenuEntry( "toggle per-pixel and per-vertex lighting  [f]", 'f' );
    glutSetMenu( main_menu );
    glutAddSubMenu( "Controls", controls );
  }

  void do_init()
  {
    std::fill(&m_action[0],&m_action[0]+256,false);

    OpenTissue::cg::startup();

    math_types::vector3_type position( 0, -1, 1);
    math_types::vector3_type target(0,0,0);
    math_types::vector3_type up(0,0,1);

    this->camera().init(position, target, up);

    //--- setup boid geometry
    OpenTissue::trimesh::TriMesh<> boid_mesh;
    OpenTissue::mesh::obj_read(demo_path + "lighting/data/boid.obj",boid_mesh);
    OpenTissue::mesh::compute_angle_weighted_vertex_normals(boid_mesh);

    mesh2geometry(boid_mesh,m_boid_geometry);

    m_vertex_program.load_from_file(
      OpenTissue::cg::Program::vertex_program,demo_path+"lighting/data/vp_fragment_lighting.cg"
      );
    m_fragment_program.load_from_file(
      OpenTissue::cg::Program::fragment_program,demo_path+"lighting/data/fp_fragment_lighting.cg"
      );
    m_perpixel_lighting = true;
  }

  void do_run(){}

  void do_shutdown()
  {
    OpenTissue::cg::shutdown();
  }

};

OpenTissue::glut::instance_pointer init_glut_application(int /*argc*/, char ** /*argv*/)
{
  OpenTissue::glut::instance_pointer instance;
  instance.reset( new Application() );
  return instance;
}
